import queue
import threading
import time
from datetime import datetime

import serial
import serial.tools.list_ports
import ttkbootstrap as ttkb
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledText

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


@annotate(ClassInfo(name="串口调试助手", category="tool"))
class SerialView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)

        # 串口相关变量
        self.ser = None
        self.is_connected = False
        self.receive_queue = queue.Queue()
        self.send_timer = None
        self.is_sending_periodically = False

        # 创建主布局
        self._create_main_layout()

        # 启动接收线程
        self.receive_thread = threading.Thread(target=self._receive_data, daemon=True)
        self.receive_thread.start()

        # 定期检查接收队列
        self._check_receive_queue()

        # 初始搜索串口
        self._search_ports()

    def _create_main_layout(self):
        """创建主布局"""
        # 顶部配置区域
        config_frame = ttkb.LabelFrame(self, text="串口配置", padding=10)
        config_frame.pack(fill=X, padx=10, pady=5)

        # 串口选择
        port_frame = ttkb.Frame(config_frame)
        port_frame.pack(fill=X, pady=5)

        ttkb.Label(port_frame, text="串口:").pack(side=LEFT, padx=5)
        self.port_var = ttkb.StringVar()
        self.port_combo = ttkb.Combobox(port_frame, textvariable=self.port_var, width=15)
        self.port_combo.pack(side=LEFT, padx=5)

        ttkb.Button(port_frame, text="搜索", command=self._search_ports).pack(side=LEFT, padx=5)
        self.connect_btn = ttkb.Button(port_frame, text="打开", command=self._toggle_connection, bootstyle=SUCCESS)
        self.connect_btn.pack(side=LEFT, padx=5)

        # 波特率等参数
        params_frame = ttkb.Frame(config_frame)
        params_frame.pack(fill=X, pady=5)

        ttkb.Label(params_frame, text="波特率:").pack(side=LEFT, padx=5)
        self.baudrate_var = ttkb.StringVar(value="9600")
        baudrate_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.baudrate_var,
            values=["9600", "19200", "38400", "57600", "115200", "230400", "460800"],
            width=10
        )
        baudrate_combo.current(4)
        baudrate_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="数据位:").pack(side=LEFT, padx=5)
        self.databits_var = ttkb.StringVar(value="8")
        databits_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.databits_var,
            values=["5", "6", "7", "8"],
            width=5
        )

        databits_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="停止位:").pack(side=LEFT, padx=5)
        self.stopbits_var = ttkb.StringVar(value="1")
        stopbits_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.stopbits_var,
            values=["1", "1.5", "2"],
            width=5
        )
        stopbits_combo.pack(side=LEFT, padx=5)

        ttkb.Label(params_frame, text="校验位:").pack(side=LEFT, padx=5)
        self.parity_var = ttkb.StringVar(value="None")
        parity_combo = ttkb.Combobox(
            params_frame,
            textvariable=self.parity_var,
            values=["None", "Odd", "Even", "Mark", "Space"],
            width=7
        )
        parity_combo.pack(side=LEFT, padx=5)

        # 中间接收区域
        receive_frame = ttkb.LabelFrame(self, text="接收日志", padding=10)
        receive_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)

        self.receive_text = ScrolledText(receive_frame, wrap=WORD, height=15)
        self.receive_text.pack(fill=BOTH, expand=True)

        # 接收控制
        receive_ctrl_frame = ttkb.Frame(receive_frame)
        receive_ctrl_frame.pack(fill=X, pady=5)

        self.timestamp_var = ttkb.BooleanVar(value=True)
        ttkb.Checkbutton(receive_ctrl_frame, text="显示时间戳", variable=self.timestamp_var).pack(side=LEFT, padx=5)

        self.hex_receive_var = ttkb.BooleanVar(value=False)
        ttkb.Checkbutton(receive_ctrl_frame, text="十六进制显示", variable=self.hex_receive_var).pack(side=LEFT, padx=5)

        ttkb.Button(receive_ctrl_frame, text="清空", command=self._clear_receive).pack(side=RIGHT, padx=5)

        # 底部发送区域
        send_frame = ttkb.LabelFrame(self, text="发送区", padding=10)
        send_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)

        # 发送模式选择
        send_mode_frame = ttkb.Frame(send_frame)
        send_mode_frame.pack(fill=X, pady=5)

        self.send_mode_var = ttkb.StringVar(value="single")
        ttkb.Radiobutton(send_mode_frame, text="单行发送", variable=self.send_mode_var, value="single").pack(side=LEFT,
                                                                                                             padx=10)
        ttkb.Radiobutton(send_mode_frame, text="多行发送", variable=self.send_mode_var, value="multi").pack(side=LEFT,
                                                                                                            padx=10)

        # 发送内容
        self.send_text = ScrolledText(send_frame, wrap=WORD, height=5)
        self.send_text.pack(fill=BOTH, expand=True, pady=5)

        # 发送控制
        send_ctrl_frame = ttkb.Frame(send_frame)
        send_ctrl_frame.pack(fill=X, pady=5)

        # 定时发送
        ttkb.Label(send_ctrl_frame, text="定时发送(ms):").pack(side=LEFT, padx=5)
        self.interval_var = ttkb.StringVar(value="1000")
        ttkb.Entry(send_ctrl_frame, textvariable=self.interval_var, width=10).pack(side=LEFT, padx=5)

        self.hex_send_var = ttkb.BooleanVar(value=False)
        ttkb.Checkbutton(send_ctrl_frame, text="十六进制发送", variable=self.hex_send_var).pack(side=LEFT, padx=5)

        self.append_crlf_var = ttkb.BooleanVar(value=True)
        ttkb.Checkbutton(send_ctrl_frame, text="自动添加换行", variable=self.append_crlf_var).pack(side=LEFT, padx=5)

        ttkb.Button(send_ctrl_frame, text="发送", command=self._send_data).pack(side=RIGHT, padx=5)
        self.periodic_btn = ttkb.Button(send_ctrl_frame, text="定时发送", command=self._toggle_periodic_send,
                                        bootstyle=WARNING)
        self.periodic_btn.pack(side=RIGHT, padx=5)
        ttkb.Button(send_ctrl_frame, text="清空", command=self._clear_send).pack(side=RIGHT, padx=5)

    def _search_ports(self):
        """搜索可用串口"""
        self.port_combo['values'] = []
        ports = serial.tools.list_ports.comports()
        port_list = [port.device for port in ports]
        if port_list:
            self.port_combo['values'] = port_list
            self.port_combo.current(0)

    def _toggle_connection(self):
        """切换串口连接状态"""
        if self.is_connected:
            # 关闭串口
            self._disconnect()
        else:
            # 打开串口
            self._connect()

    def _connect(self):
        """连接串口"""
        try:
            port = self.port_var.get()
            baudrate = int(self.baudrate_var.get())

            # 数据位转换
            databits = int(self.databits_var.get())
            if databits == 5:
                databits = serial.FIVEBITS
            elif databits == 6:
                databits = serial.SIXBITS
            elif databits == 7:
                databits = serial.SEVENBITS
            else:
                databits = serial.EIGHTBITS

            # 停止位转换
            stopbits = float(self.stopbits_var.get())
            if stopbits == 1:
                stopbits = serial.STOPBITS_ONE
            elif stopbits == 1.5:
                stopbits = serial.STOPBITS_ONE_POINT_FIVE
            else:
                stopbits = serial.STOPBITS_TWO

            # 校验位转换
            parity = self.parity_var.get().lower()
            if parity == "none":
                parity = serial.PARITY_NONE
            elif parity == "odd":
                parity = serial.PARITY_ODD
            elif parity == "even":
                parity = serial.PARITY_EVEN
            elif parity == "mark":
                parity = serial.PARITY_MARK
            else:  # space
                parity = serial.PARITY_SPACE

            # 打开串口
            self.ser = serial.Serial(
                port=port,
                baudrate=baudrate,
                bytesize=databits,
                stopbits=stopbits,
                parity=parity,
                timeout=0.1
            )

            if self.ser.is_open:
                self.is_connected = True
                self.connect_btn.config(text="关闭", bootstyle=DANGER)
                self._add_receive_log(f"已连接到 {port}，波特率 {baudrate}")
                # 禁用配置项
                # self.port_combo.config(state=DISABLED)
                self.baudrate_var.set(str(baudrate))
                self.baudrate_var.set(str(baudrate))
                # self.baudrate_combo.config(state=DISABLED)
                # self.databits_combo.config(state=DISABLED)
                # self.stopbits_combo.config(state=DISABLED)
                # self.parity_combo.config(state=DISABLED)

        except Exception as e:
            self._add_receive_log(f"连接失败: {str(e)}", is_error=True)

    def _disconnect(self):
        """断开串口连接"""
        if self.ser and self.ser.is_open:
            self.ser.close()

        self.is_connected = False
        self.connect_btn.config(text="打开", bootstyle=SUCCESS)
        self._add_receive_log("已断开连接")

        # 停止定时发送
        if self.is_sending_periodically:
            self._toggle_periodic_send()

        # 启用配置项
        self.port_combo.config(state=NORMAL)
        # self.baudrate_combo.config(state=NORMAL)
        # self.databits_combo.config(state=NORMAL)
        # self.stopbits_combo.config(state=NORMAL)
        # self.parity_combo.config(state=NORMAL)

    def _send_data(self):
        """发送数据"""
        if not self.is_connected:
            self._add_receive_log("未连接到串口", is_error=True)
            return

        try:
            # 获取发送内容
            send_mode = self.send_mode_var.get()
            if send_mode == "single":
                data = self.send_text.get("1.0", "1.0 lineend")  # 单行
            else:
                data = self.send_text.get("1.0", END)  # 多行

            # 清除末尾的换行符
            data = data.rstrip('\n').rstrip('\r')

            # 自动添加换行
            if self.append_crlf_var.get():
                data += '\r\n\0'

            # 十六进制发送处理
            if self.hex_send_var.get():
                # 移除所有空格
                data = data.replace(' ', '')
                # 转换为字节
                send_bytes = bytes.fromhex(data)
            else:
                # 字符串编码为字节
                send_bytes = data.encode('utf-8')

            # 发送数据
            self.ser.write(send_bytes)

            # 显示发送的内容
            if self.hex_send_var.get():
                hex_str = ' '.join([f'{b:02X}' for b in send_bytes])
                self._add_receive_log(f"发送: {hex_str} (HEX)", is_send=True)
            else:
                self._add_receive_log(f"发送: {data}", is_send=True)

        except Exception as e:
            self._add_receive_log(f"发送失败: {str(e)}", is_error=True)

    def _toggle_periodic_send(self):
        """切换定时发送状态"""
        if self.is_sending_periodically:
            # 停止定时发送
            self.is_sending_periodically = False
            self.periodic_btn.config(text="定时发送", bootstyle=WARNING)
            self._add_receive_log("已停止定时发送")
        else:
            # 开始定时发送
            try:
                interval = int(self.interval_var.get())
                if interval <= 0:
                    raise ValueError("间隔必须大于0")

                self.is_sending_periodically = True
                self.periodic_btn.config(text="停止发送", bootstyle=DANGER)
                self._add_receive_log(f"开始定时发送，间隔 {interval}ms")

                # 启动定时发送线程
                def periodic_send():
                    while self.is_sending_periodically and self.is_connected:
                        self._send_data()
                        time.sleep(interval / 1000.0)

                threading.Thread(target=periodic_send, daemon=True).start()

            except ValueError as e:
                self._add_receive_log(f"定时发送设置错误: {str(e)}", is_error=True)
            except Exception as e:
                self._add_receive_log(f"启动定时发送失败: {str(e)}", is_error=True)
                self.is_sending_periodically = False

    def _receive_data(self):
        """接收数据线程"""
        while True:
            if self.is_connected and self.ser and self.ser.is_open:
                try:
                    # 读取数据
                    if self.ser.in_waiting > 0:
                        data = self.ser.read(self.ser.in_waiting)
                        self.receive_queue.put(data)
                except Exception as e:
                    if self.is_connected:  # 只有在连接状态下才报告错误
                        self.receive_queue.put(f"接收错误: {str(e)}")
                    time.sleep(0.1)
            else:
                time.sleep(0.1)

    def _check_receive_queue(self):
        """检查接收队列并更新UI"""
        while not self.receive_queue.empty():
            item = self.receive_queue.get()
            if isinstance(item, bytes):
                # 处理接收到的字节数据
                self._process_received_data(item)
            else:
                # 处理错误消息
                self._add_receive_log(item, is_error=True)
            self.receive_queue.task_done()

        # 定期检查
        self.after(100, self._check_receive_queue)

    def _process_received_data(self, data):
        """处理接收到的数据"""
        if self.hex_receive_var.get():
            # 十六进制显示
            hex_str = ' '.join([f'{b:02X}' for b in data])
            self._add_receive_log(f"接收: {hex_str} (HEX)")
        else:
            # 字符串显示
            try:
                text = data.decode('utf-8')
                self._add_receive_log(f"接收: {text}")
            except UnicodeDecodeError:
                # 解码失败时显示十六进制
                hex_str = ' '.join([f'{b:02X}' for b in data])
                self._add_receive_log(f"接收: {hex_str} (无法解码为UTF-8)")

    def _add_receive_log(self, message, is_error=False, is_send=False):
        """添加接收日志"""
        # 获取当前时间戳
        timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]

        # 格式化消息
        if self.timestamp_var.get():
            log_message = f"[{timestamp}] {message}\n"
        else:
            log_message = f"{message}\n"

        # 更新UI
        # self.receive_text.config(state=NORMAL)

        # 根据消息类型设置颜色
        if is_error:
            self.receive_text.insert(END, log_message, "error")
        elif is_send:
            self.receive_text.insert(END, log_message, "send")
        else:
            self.receive_text.insert(END, log_message)

        # 自动滚动到底部
        self.receive_text.see(END)
        # self.receive_text.config(state=DISABLED)

        # 配置标签样式
        self.receive_text.tag_config("error", foreground="red")
        self.receive_text.tag_config("send", foreground="#4CAF50")

    def _clear_receive(self):
        """清空接收区"""
        # self.receive_text.config(state=NORMAL)
        self.receive_text.delete("1.0", END)
        # self.receive_text.config(state=DISABLED)

    def _clear_send(self):
        """清空发送区"""
        self.send_text.delete("1.0", END)

    def on_close(self):
        """窗口关闭时的处理"""
        self.is_sending_periodically = False
        if self.is_connected:
            self._disconnect()
        self.destroy()

    def create_widgets(self):
        pass
